﻿using System.Diagnostics;
using DimensionsHelper.Common.Utils;
using DimensionsHelper.LanguageService.Symbols;
using DimensionsHelper.LanguageService.Syntax;

namespace DimensionsHelper.LanguageService;

/// <summary>
///     绑定声明文件中的语法符号定义。包括Module、Interface、Class、Enum、Function
///     定义。
/// </summary>
public class DeclarationBinder(WorkFolder workFolder) : Binder(workFolder)
{
    private readonly List<PendingObjectType> _pendingObjectTypes = [];
    private readonly List<PendingTypeBinding> _pendingTypeBindings = [];

    private AnalysisTreeSymbolTable? _treeSymbolTable;


    /// <summary>
    ///     绑定声明语法树中的所有语法符号，并声明到语法树绑定的符号表中。
    /// </summary>
    /// <param name="syntaxTree">待绑定的语法树</param>
    protected override AnalysisTree BindTreeCore(SyntaxTree syntaxTree)
    {
        Debug.Assert(syntaxTree.IsDeclare);

        AnalysisTree tree = WorkFolder.GetAnalysisTree(syntaxTree);

        _treeSymbolTable = tree.Symbols;
        // Statements
        syntaxTree.GetNodes().ForEach(stat =>
            tree.AddChild(BindStatement(stat)));

        return tree;
    }


    protected override void OnBindSingleTreeCompletedOverride()
    {
        WorkFolder.GetWorkFolderSymbolTable().GetDeclaredSymbols().Join(_treeSymbolTable!);
        _treeSymbolTable = null;
    }


    protected override void OnBindCompletedOverride()
    {
        _pendingObjectTypes.ForEach(BindPendingObjectType);
        _pendingTypeBindings.ForEach(BindPendingTypeBinding);

        _pendingObjectTypes.Clear();
        _pendingTypeBindings.Clear();
    }


    private ITypeSymbol? LookupNamedType(string name, ModuleSymbol? moduleSymbol)
    {
        // 优先查找值类型
        if (moduleSymbol == null && ValueTypeSymbol.TryGetValueSymbol(name, out ValueTypeSymbol? valueTypeSymbol))
        {
            return valueTypeSymbol;
        }

        ModuleSymbol? containingModule = moduleSymbol;
        // 在模块中向上查找
        while (containingModule != null)
        {
            ITypeSymbol? found = containingModule.LookupNamedMember(name);

            if (found != null)
            {
                return found;
            }

            containingModule = containingModule.ContainingModule;
        }

        // 在工作文件夹和全局声明表中查找
        return WorkFolder.GetWorkFolderSymbolTable().LookupDeclaredSymbol(name) as ITypeSymbol
               ?? GlobalSymbols.LookupDeclaredSymbol(name) as ITypeSymbol;
    }


    private void BindPendingObjectType(PendingObjectType pendingObjectType)
    {
        AnalysisObjectTypeSignature signature = pendingObjectType.Signature;
        NamedTypeSymbol symbol = pendingObjectType.TypeSymbol;

        // Inherits/Implements/Properties/Methods/Constants
        signature.GetChildren().ForEach(child =>
        {
            switch (child)
            {
                // QualifiedNames
                case { BoundKind: AnalysisBoundKinds.QualifiedName }:
                {
                    AnalysisQualifiedName qualifiedName = (AnalysisQualifiedName)child;
                    BindQualifiedNames(qualifiedName, symbol.ContainingModule);
                    ITypeSymbol typeSymbol = qualifiedName.GetTypeSymbol();

                    if (!typeSymbol.IsUndefined)
                    {
                        // Inheirts
                        if (qualifiedName.IsInheritClassName || qualifiedName.IsInheritInterfaceName)
                        {
                            if (!symbol.Inherit(typeSymbol))
                            {
                                Raise(DiagnosticDescriptors.RepetitiveInheirtInterface,
                                    qualifiedName.Syntax, typeSymbol.FullName);
                            }
                        }
                        // Implements
                        else if (qualifiedName.IsImplementInterfaceName)
                        {
                            if (!symbol.Implement(typeSymbol))
                            {
                                Raise(DiagnosticDescriptors.RepetitiveImplementInterface,
                                    qualifiedName.Syntax, typeSymbol.FullName);
                            }
                        }
                    }

                    break;
                }

                // Property
                case { BoundKind: AnalysisBoundKinds.PropertySignature }:
                {
                    UpdateProperty((AnalysisPropertySignature)child, symbol);
                    break;
                }

                // Method
                case { BoundKind: AnalysisBoundKinds.MethodSignature }:
                {
                    UpdateMethod((AnalysisMethodSignature)child, symbol);
                    break;
                }

                // Constant
                case { BoundKind: AnalysisBoundKinds.ConstantSignature }:
                {
                    Debug.Assert(signature.Syntax.IsNode(SyntaxKinds.ClassSignature));
                    UpdateConstant((AnalysisConstantSignature)child, symbol);
                    break;
                }
            }
        });
    }


    private void BindQualifiedNames(AnalysisQualifiedName qualifiedName, ModuleSymbol? containingModule)
    {
        ITypeSymbol? symbol = null;
        string currentName = string.Empty;

        foreach (AnalysisIdentifier? name in qualifiedName.GetChildren().Cast<AnalysisIdentifier>())
        {
            symbol = symbol == null
                ? LookupNamedType(name.Syntax.Value, containingModule)
                : symbol.LookupNamedMember(name.Syntax.Value);

            // 如果未找到，跳出
            if (symbol == null)
            {
                if (string.IsNullOrEmpty(currentName))
                {
                    Raise(DiagnosticDescriptors.UnknownName, name.Syntax, name.Syntax.Value);
                }
                else
                {
                    Raise(DiagnosticDescriptors.UnknownNameInModule, name.Syntax, name.Syntax.Value, name);
                }

                return;
            }

            name.Bind(symbol);
            currentName = symbol.FullName;
        }

        if (symbol != null)
        {
            qualifiedName.Bind(symbol);
        }
        else
        {
            qualifiedName.Bind(UndefinedSymbol.Default);
        }
    }


    private void BindPendingTypeBinding(PendingTypeBinding pendingTypeBinding)
    {
        BindQualifiedNames(pendingTypeBinding.Binding.QualifiedName, pendingTypeBinding.ContainingModule);
    }


    private static bool IsValueTypeBinding(AnalysisTypeBinding binding)
    {
        if (binding.QualifiedName.GetChildren().ToArray() is [AnalysisIdentifier typeId])
        {
            return ValueTypeSymbol.TryGetValueSymbol(typeId.Syntax.Value, out _);
        }

        return false;
    }


    private void PendTypeBinding(AnalysisTypeBinding binding, ModuleSymbol? containingModule)
    {
        if (!IsValueTypeBinding(binding))
        {
            _pendingTypeBindings.Add(new PendingTypeBinding(binding, containingModule));
        }
    }


    private ParameterSymbol BindParameter(
        AnalysisParameterSignature signature,
        BindableSymbolWithParameters containingSymbol)
    {
        ParameterSymbol symbol = new(signature.Syntax, containingSymbol);
        signature.NameIdentifier.Bind(symbol);

        if (!containingSymbol.DeclareLocalVariable(symbol))
        {
            Raise(DiagnosticDescriptors.RepetitiveParameterSignature,
                signature.NameIdentifier.Syntax,
                signature.NameIdentifier.Syntax.Value);
        }

        PendTypeBinding(signature.Binding, containingSymbol.ContainingModule);
        return symbol;
    }


    private void BindSymbolParameters<T>(
        T node,
        BindableSymbolWithParameters symbol,
        AbstractTypeSymbol? containingSymbol) where T : AnalysisSignatureWithParameter
    {
        node.NameIdentifier.Bind(symbol);

        // Parameters
        ParameterSignature? lastParameter = null;

        node.GetChildren()
            .Where(child => child.BoundKind == AnalysisBoundKinds.ParameterSignature)
            .ForEach(p =>
            {
                AnalysisParameterSignature signature = (AnalysisParameterSignature)p;
                ParameterSymbol parameter = BindParameter(signature, symbol);
                if (!parameter.IsOptional && lastParameter?.OptionalKeywordToken != null)
                {
                    Raise(DiagnosticDescriptors.OptionalParameterShouldBePlacedAtTheEnd,
                        lastParameter.OptionalKeywordToken);
                }

                lastParameter = signature.Syntax;
            });

        PendTypeBinding(node.Binding, containingSymbol?.ContainingModule as ModuleSymbol);
    }


    private void UpdateProperty(
        AnalysisPropertySignature signature,
        NamedTypeSymbol containingType)
    {
        PropertySymbol symbol = new(containingType, signature.Syntax);

        if (symbol.IsDefault && containingType.GetDefaultProperty() != null)
        {
            Raise(DiagnosticDescriptors.RepetitiveDefaultProperty,
                signature.Syntax.NameIdentifier,
                signature.Syntax.Name);
        }
        else if (!containingType.TryAddMember(symbol))
        {
            Raise(DiagnosticDescriptors.RepetitivePropertySignature,
                signature.Syntax.NameIdentifier,
                signature.Syntax.Name);
        }

        BindSymbolParameters(signature, symbol, containingType);
    }


    private void UpdateMethod(
        AnalysisMethodSignature signature,
        NamedTypeSymbol containingType)
    {
        MethodSymbol symbol = new(containingType, signature.Syntax);

        if (symbol.IsDefault && containingType.GetDefaultMethod() != null)
        {
            Raise(DiagnosticDescriptors.RepetitiveDefaultMethod,
                signature.Syntax.NameIdentifier,
                signature.Syntax.Name);
        }
        else if (!containingType.TryAddMember(symbol))
        {
            Raise(DiagnosticDescriptors.RepetitiveMethodSignature,
                signature.Syntax.NameIdentifier,
                signature.Syntax.Name);
        }

        BindSymbolParameters(signature, symbol, containingType);
    }


    private void UpdateConstant(
        AnalysisConstantSignature signature,
        NamedTypeSymbol containingType)
    {
        LocalSymbol symbol = new(signature.Syntax, containingType, containingType.ContainingModule);
        signature.NameIdentifier.Bind(symbol);

        if (!containingType.TryAddMember(symbol))
        {
            Raise(DiagnosticDescriptors.RepetitiveConstantDeclaration,
                signature.Syntax.NameIdentifier,
                signature.Syntax.Name);
        }

        PendTypeBinding(signature.Binding, containingType.ContainingModule);
    }


    private AnalysisConstantSignature BindConstant(
        ConstantSignature signature,
        AbstractTypeSymbol? containingType)
    {
        LocalSymbol symbol = new(signature,
            containingType,
            containingType?.ContainingModule as ModuleSymbol);

        AnalysisConstantSignature node = AnalysisNodeFactory.ConstantSignature(signature, null);

        if ((containingType != null && !containingType.TryAddMember(symbol)) ||
            (containingType == null && !_treeSymbolTable!.DeclareSymbol(symbol)))
        {
            Raise(DiagnosticDescriptors.RepetitiveConstantDeclaration,
                signature.NameIdentifier,
                signature.NameIdentifier.Value);
        }

        PendTypeBinding(node.Binding, 
            containingType is ModuleSymbol module 
            ? module 
            : containingType?.ContainingModule as ModuleSymbol);

        return node;
    }


    private AnalysisStatement BindFunction(
        FunctionSignature signature,
        ModuleSymbol? containingModule)
    {
        if (Identifier.IsDefaultOrEmpty(signature.NameIdentifier))
        {
            return AnalysisEmptyStatement.Default;
        }

        FunctionSymbol symbol = new(signature, containingModule);
        AnalysisFunctionSignature node = AnalysisNodeFactory.FunctionSignature(signature, null);
        BindSymbolParameters(node, symbol, containingModule);

        if ((containingModule != null && !containingModule.TryAddMember(symbol)) ||
            (containingModule == null && !_treeSymbolTable!.DeclareSymbol(symbol)))
        {
            Raise(DiagnosticDescriptors.RepetitiveFunctionDecaration,
                signature.NameIdentifier,
                signature.Name);
        }

        return node;
    }


    private void BindEnumMember(
        AnalysisEnumMemberSignature signature,
        EnumSymbol containingType,
        ref int currentValue)
    {
        EnumMemberSymbol symbol = new(containingType, signature.Syntax);
        // 检查当前成员初始化值
        if (signature.Syntax is { EqualToken: not null, InitialValue: not null })
        {
            currentValue = signature.Syntax.InitialValue.Value;

            if (containingType.ContainsValue(currentValue))
            {
                Raise(DiagnosticDescriptors.RepetitiveEnumValue, signature.Syntax.InitialValue);
            }
        }

        // 更新枚举值
        signature.Syntax.Value = currentValue;

        if (!containingType.TryAddMember(symbol))
        {
            Raise(DiagnosticDescriptors.RepetitiveEnumMemberSignature,
                signature.NameIdentifier.Syntax,
                signature.NameIdentifier.Syntax.Value);
        }

        currentValue++;
    }


    private AnalysisStatement BindEnum(EnumSignature signature, ModuleSymbol? containingModule)
    {
        if (Identifier.IsDefaultOrEmpty(signature.NameIdentifier))
        {
            return AnalysisEmptyStatement.Default;
        }

        EnumSymbol symbol = new(signature, containingModule);
        AnalysisEnumSignature node = AnalysisNodeFactory.EnumSignature(signature, symbol, null);
        int currentValue = 0;

        node.GetChildren()
            .Where(child => child.BoundKind == AnalysisBoundKinds.EnumMemberSignature)
            .ForEach(e => BindEnumMember((AnalysisEnumMemberSignature)e, symbol, ref currentValue));

        if ((containingModule != null && !containingModule.TryAddMember(symbol)) ||
            (containingModule == null && !_treeSymbolTable!.DeclareSymbol(symbol)))
        {
            Raise(DiagnosticDescriptors.RepetitiveEnumSignature,
                signature.NameIdentifier,
                signature.NameIdentifier.Value);
        }

        return node;
    }


    private void DeclareSymbol(
        AnalysisDeclaration declaration,
        ITypeSymbol symbol,
        AbstractTypeSymbol? containingSymbol,
        DiagnosticDescriptor descriptor,
        params object?[] args)
    {
        declaration.NameIdentifier.Bind(symbol);

        if ((containingSymbol != null && !containingSymbol.TryAddMember(symbol)) ||
            (containingSymbol == null && !_treeSymbolTable!.DeclareSymbol(symbol)))
        {
            Raise(descriptor, declaration.NameIdentifier.Syntax, args);
        }
    }


    private AnalysisStatement BindObjectType(
        ObjectTypeSignature signature,
        ModuleSymbol? containingModule)
    {
        if (Identifier.IsDefaultOrEmpty(signature.NameIdentifier))
        {
            return AnalysisEmptyStatement.Default;
        }

        NamedTypeSymbol symbol = new(signature, containingModule);
        AnalysisObjectTypeSignature analysisNode = AnalysisNodeFactory.ObjectTypeSignature(signature, symbol, null);

        DeclareSymbol(analysisNode, symbol, containingModule,
            DiagnosticDescriptors.RepetitiveTypeSignature,
            signature.NameIdentifier.Value);

        _pendingObjectTypes.Add(new PendingObjectType(analysisNode, symbol));
        return analysisNode;
    }


    private static ModuleSignature CreateModuleSignatureFromIdentifier(
        SyntaxToken declareKeywordToken,
        Identifier identifier)
    {
        QualifiedName qualified = NodeFactory.CreateQualifiedName(null, identifier);
        qualified.UpdateFrom(identifier);
        // module
        ModuleSignature module = NodeFactory.CreateModuleSignature(null, declareKeywordToken, qualified,
            null, null, null, null, null);
        module.UpdateFrom(identifier);
        return module;
    }


    private ModuleSymbol DeclareModuleFromIdentifier(
        SyntaxToken declareToken,
        Identifier identifier,
        ModuleSymbol? containingModule)
    {
        ModuleSignature signature = CreateModuleSignatureFromIdentifier(declareToken, identifier);
        ModuleSymbol moduleSymbol = new(signature);

        if (containingModule != null)
        {
            containingModule.TryAddMember(moduleSymbol);
        }
        else
        {
            _treeSymbolTable?.DeclareSymbol(moduleSymbol);
        }

        return moduleSymbol;
    }


    private ModuleSymbol LookupOrDeclareModule(ModuleSignature signature)
    {
        QualifiedName qualifiedName = signature.QualifiedName;
        ModuleSymbol? currentModule = null;
        qualifiedName.ForEach(id =>
        {
            if (currentModule == null)
            {
                currentModule =
                    _treeSymbolTable?.LookupDeclaredSymbol(id.Value) as ModuleSymbol ??
                    WorkFolder.GetWorkFolderSymbolTable().LookupDeclaredSymbol(id.Value) as ModuleSymbol ??
                    GlobalSymbols.LookupDeclaredSymbol(id.Value) as ModuleSymbol ??
                    DeclareModuleFromIdentifier((SyntaxToken)signature.DeclareKeywordToken, id, null);
            }
            else
            {
                currentModule =
                    currentModule.LookupNamedMember(id.Value) as ModuleSymbol ??
                    DeclareModuleFromIdentifier((SyntaxToken)signature.DeclareKeywordToken, id, currentModule);
            }
        });

        Debug.Assert(currentModule != null, $"QualifiedName is empty: {qualifiedName}");
        return currentModule;
    }


    private AnalysisStatement BindModule(ModuleSignature signature)
    {
        // 忽略掉全限定名是空的错误模块定义
        if (signature.QualifiedName.IsEmpty)
        {
            return AnalysisEmptyStatement.Default;
        }

        ModuleSymbol symbol = LookupOrDeclareModule(signature);
        AnalysisModuleSignature node = AnalysisNodeFactory.ModuleSignature(signature, symbol, null);
        // Bind QualifiedName
        IEnumerable<AnalysisNode> qualifiedNames = node.QualifiedName.GetChildren().Reverse();
        ModuleSymbol? currentModule = symbol;
        foreach (AnalysisNode? qualifiedName in qualifiedNames)
        {
            if (currentModule != null)
            {
                qualifiedName.Bind(currentModule);
                currentModule = currentModule.ContainingModule;
            }
            else
            {
                break;
            }
        }

        // Children
        // Classes
        signature.GetClasses()
            .Where(IsSignatureValid)
            .ForEach(c => node.AddChild(BindObjectType(c, symbol)));
        // Interfaces
        signature.GetInterfaces()
            .Where(IsSignatureValid)
            .ForEach(i => node.AddChild(BindObjectType(i, symbol)));
        // Enums
        signature.GetEnums()
            .Where(IsSignatureValid)
            .ForEach(e => node.AddChild(BindEnum(e, symbol)));
        // Functions
        signature.GetFunctions()
            .Where(IsSignatureValid)
            .ForEach(f => node.AddChild(BindFunction(f, symbol)));
        // Constants
        signature.GetConstants()
            .Where(IsSignatureValid)
            .ForEach(c => node.AddChild(BindConstant(c, symbol)));

        return node;

        static bool IsSignatureValid(Declaration declaration)
        {
            return !Identifier.IsDefaultOrEmpty(declaration.NameIdentifier);
        }
    }


    private AnalysisStatement BindStatement(Statement statement)
    {
        return statement.Kind switch
        {
            // Module
            SyntaxKinds.ModuleSignature => BindModule((ModuleSignature)statement),
            // Class, Interface
            SyntaxKinds.ClassSignature or SyntaxKinds.InterfaceSignature =>
                BindObjectType((ObjectTypeSignature)statement, null),
            // Enum
            SyntaxKinds.EnumSignature => BindEnum((EnumSignature)statement, null),
            // Function
            SyntaxKinds.FunctionSignature => BindFunction((FunctionSignature)statement, null),
            // Constant
            SyntaxKinds.ConstantSignature => BindConstant((ConstantSignature)statement, null),
            // Not supported
            _ => AnalysisEmptyStatement.Default
        };
    }


    /// <summary>
    ///     绑定带有 As TypeName 这样的类型绑定声明时，需要在所有类型声明完成后再进行。
    /// </summary>
    /// <param name="Binding">类型绑定声明节点</param>
    /// <param name="ContainingModule">所在的模块符号</param>
    private record PendingTypeBinding(AnalysisTypeBinding Binding, ModuleSymbol? ContainingModule);


    /// <summary>
    ///     绑定类型声明Class或Interface时，第一次遍历节点时，只对名称进行声明，
    ///     添加基础的类型符号，不检查其内部的属性、方法和常量。此类用于暂时保存类型信息，
    ///     在符号添加完成后再进行绑定。
    /// </summary>
    /// <param name="Signature">类型声明节点</param>
    /// <param name="TypeSymbol">类型符号</param>
    private record PendingObjectType(AnalysisObjectTypeSignature Signature, NamedTypeSymbol TypeSymbol);
}